#! /usr/bin/env python
#
"""Utilities for caching computation results.
"""
__docformat__ = 'reStructuredText'


from time import time

from iterators cimport Iterator
from native cimport dict, list

## auxiliary classes

cdef class _TimeBasedUnique(Iterator):
    """Return a new unique ID at each iteration step.

    The returned ID is the number of nanoseconds elapsed since the
    system epoch.  The returned ID is guaranteed to be monotonically
    increasing::

      >>> u = _TimeBasedUnique()
      >>> u.next() < u.next() < u.next()
      True

    """
    def __next__(self):
        return int(time() * 1000000)

cdef _TimeBasedUnique _unique = _TimeBasedUnique()


cdef class Cacheable(object):
    """Instances of this class provide an interface for use by caches
    based on a `dict` subclass.
    """

    def __init__(self):
        self.__id = _unique.next()
        self.__refs = list()

    cpdef cache_id(self):
        """Return a integer value which is unique and guaranteed not
        to be re-used.
        
        (In contrast, Python's standard `id()` function returns a
        value based on the intance memory address, which *can* be
        re-used during the lifetime of a program.)
        """
        return self.__id

    cpdef reference(self, cache, key):
        """Register a reference to this object from a cache."""
        self.__refs.append((cache, key))

    cpdef release(self):
        """Delete all references to this object held in caches."""
        cdef dict cache
        for (cache, key) in self.__refs:
            try:
                del cache[key]
            except KeyError:
                pass # might have been released by some other object
    

cpdef cache_id(o):
    try:
        return o.cache_id()
    except AttributeError:
        return id(o)
